using System;
using CQRS.Events;
using CQRS.Events.DomainClass;
using CQRS.Events.Memento;
using CQRS.Sample.Domain.Events;

namespace CQRS.Sample.Domain.BOs
{
    public class Plane : AggregateRoot<Event>, IOriginator
    {
        public Plane()
        {
        }

        public Plane(string planeName, string manufacturer, long yearManufactured, long flightsMade, Pilot pilot)
        {
            PlaneName = planeName;
            Manufacturer = manufacturer;
            YearManufactured = yearManufactured;
            FlightsMade = flightsMade;
            Pilot = pilot;
            HasCrashed = false;
            Apply(new PlaneCreatedEvent(EventProviderId, PlaneName, Manufacturer, YearManufactured, FlightsMade,
                                        pilot.Name, pilot.Surname, pilot.FlightsMade));
        }

        public string Manufacturer { get; private set; }

        public long YearManufactured { get; private set; }

        public string PlaneName { get; private set; }
        public long FlightsMade { get; private set; }
        public Pilot Pilot { get; private set; }
        public bool HasCrashed { get; private set; }

        #region IOriginator Members

        public IMemento CreateMemento()
        {
            return new PlaneMemento(PlaneName, Manufacturer, YearManufactured, FlightsMade, Pilot.Name, Pilot.Surname,
                                    Pilot.FlightsMade, HasCrashed);
        }

        public void SetMemento(IMemento memento)
        {
            var planeMemento = memento as PlaneMemento;
            if (planeMemento == null)
                throw new ArgumentException("Wrong type of memento passed");

            PlaneName = planeMemento.PlaneName;
            Manufacturer = planeMemento.Manufacturer;
            YearManufactured = planeMemento.YearManufactured;
            HasCrashed = planeMemento.HasCrashed;
            Pilot = new Pilot(planeMemento.PilotName, planeMemento.PilotSurname, planeMemento.FlightsMadeByPilot);
        }

        #endregion

        protected override void RegisterEvents()
        {
            RegisterEvent<PlaneCreatedEvent>(OnPlaneCreated);
            RegisterEvent<PlaneHasCrashedEvent>(OnPlaneCrashed);
            RegisterEvent<PlaneEmergencyOccuredEvent>(OnEmergencyOccured);
            RegisterEvent<PlaneRepairedEvent>(OnPlaneRepaired);
        }

        private void OnEmergencyOccured(PlaneEmergencyOccuredEvent planeHasCrashedEvent)
        {
        }

        private void OnPlaneCrashed(PlaneHasCrashedEvent planeHasCrashedEvent)
        {
            HasCrashed = true;
        }

        private void OnPlaneRepaired(PlaneRepairedEvent planeRepairedEvent)
        {
        }

        private void HasNotCrashed()
        {
            if (HasCrashed)
                throw new ArgumentException("Cannot register this event for crashed plane.");
        }

        private void OnPlaneCreated(PlaneCreatedEvent planeCreatedEvent)
        {
            Id = planeCreatedEvent.AggregateId;
            Pilot = new Pilot(planeCreatedEvent.PilotName, planeCreatedEvent.PilotSurname,
                              planeCreatedEvent.FlightsMadeByPilot);
        }

        public void PlaneCrashed(Guid planeId, string location, string route,
                                 long fatalities, long aboard, DateTime crashDate)
        {
            HasNotCrashed();
            Apply(new PlaneHasCrashedEvent(planeId, location, fatalities, aboard, route, crashDate));
        }

        internal void EmergencyRegister(Guid planeId, string airportName,
                                        string crewCommander, DateTime emergencyDate, string flightNumber,
                                        string weatherConditions)
        {
            HasNotCrashed();
            Apply(new PlaneEmergencyOccuredEvent(planeId, flightNumber, airportName, crewCommander, emergencyDate,
                                                 weatherConditions));
        }

        internal void PlaneRepaired(Guid planeId, string effectuatedBy, DateTime repairDate,
                                    string changedPart, string repairedPart)
        {
            HasNotCrashed();
            Apply(new PlaneRepairedEvent(planeId,
                                         effectuatedBy, repairDate, repairedPart, changedPart));
        }
    }
}